Recientemente comparé las velocidades de procesamiento de [] y list() y me sorprendió descubrir que [] se ejecuta tres veces más rápido que list() . Realicé la misma prueba con {} y dict() y los resultados fueron prácticamente idénticos: [] y {} tardaron alrededor de 0,128 s/millón de ciclos, mientras que list() y dict() tardaron aproximadamente 0,428 s/millón de ciclos cada uno.
¿Por qué es esto? Haga [] y {} (y probablemente () y '' , también) devuelva inmediatamente copias de algún literal de stock vacío mientras sus contrapartes nombradas explícitamente ( list() , dict() , tuple() , str() ) emprender completamente la creación de un objeto, tengan o no elementos?
No tengo idea de en qué se diferencian estos dos métodos, pero me encantaría averiguarlo. No pude encontrar una respuesta en los documentos o en SO, y buscar corchetes vacíos resultó ser más problemático de lo que esperaba.
Obtuve mis resultados de tiempo llamando a timeit.timeit("[]") y timeit.timeit("list()") , y timeit.timeit("{}") y timeit.timeit("dict()") , para comparar listas y diccionarios, respectivamente. Estoy ejecutando Python 2.7.9.
Recientemente descubrí " ¿Por qué if True es más lento que if 1? " que compara el rendimiento de if True con if 1 y parece tocar un escenario similar de literal versus global; tal vez valga la pena considerarlo también.
Porque la list es una función para convertir, digamos, una cadena en un objeto de lista, mientras que [] se usa para crear una lista desde el principio. Prueba esto (podría tener más sentido para ti):
x = "wham bam" a = list(x) >>> a ["w", "h", "a", "m", ...]Tiempo
y = ["wham bam"] >>> y ["wham bam"]Te da una lista real que contiene todo lo que pones en ella.
Porque [] y {} son sintaxis literal . Python puede crear código de bytes solo para crear la lista o los objetos del diccionario:
>>> import dis >>> dis.dis(compile('[]', '', 'eval')) 1 0 BUILD_LIST 0 3 RETURN_VALUE >>> dis.dis(compile('{}', '', 'eval')) 1 0 BUILD_MAP 0 3 RETURN_VALUE list() y dict() son objetos separados. Sus nombres deben resolverse, la pila debe participar para impulsar los argumentos, el marco debe almacenarse para recuperarlo más tarde y debe realizarse una llamada. Todo eso lleva más tiempo.
Para el caso vacío, eso significa que tiene al menos un LOAD_NAME (que tiene que buscar a través del espacio de nombres global, así como el módulo builtins ) seguido de una CALL_FUNCTION , que tiene que conservar el cuadro actual:
>>> dis.dis(compile('list()', '', 'eval')) 1 0 LOAD_NAME 0 (list) 3 CALL_FUNCTION 0 6 RETURN_VALUE >>> dis.dis(compile('dict()', '', 'eval')) 1 0 LOAD_NAME 0 (dict) 3 CALL_FUNCTION 0 6 RETURN_VALUE Puede cronometrar la búsqueda de nombres por separado con timeit :
>>> import timeit >>> timeit.timeit('list', number=10**7) 0.30749011039733887 >>> timeit.timeit('dict', number=10**7) 0.4215109348297119La discrepancia de tiempo probablemente sea una colisión de hash de diccionario. Reste esos tiempos de los tiempos para llamar a esos objetos y compare el resultado con los tiempos para usar literales:
>>> timeit.timeit('[]', number=10**7) 0.30478692054748535 >>> timeit.timeit('{}', number=10**7) 0.31482696533203125 >>> timeit.timeit('list()', number=10**7) 0.9991960525512695 >>> timeit.timeit('dict()', number=10**7) 1.0200958251953125 Entonces, tener que llamar al objeto toma 1.00 - 0.31 - 0.30 == 0.39 segundos adicionales por cada 10 millones de llamadas.
Puede evitar el costo de la búsqueda global creando un alias de los nombres globales como locales (usando una configuración de timeit , todo lo que vincula a un nombre es local):
>>> timeit.timeit('_list', '_list = list', number=10**7) 0.1866450309753418 >>> timeit.timeit('_dict', '_dict = dict', number=10**7) 0.19016098976135254 >>> timeit.timeit('_list()', '_list = list', number=10**7) 0.841480016708374 >>> timeit.timeit('_dict()', '_dict = dict', number=10**7) 0.7233691215515137 pero nunca puedes superar ese costo de CALL_FUNCTION .
list() requiere una búsqueda global y una llamada de función, pero [] se compila en una sola instrucción. Ver:
Python 2.7.3 >>> import dis >>> dis.dis(lambda: list()) 1 0 LOAD_GLOBAL 0 (list) 3 CALL_FUNCTION 0 6 RETURN_VALUE >>> dis.dis(lambda: []) 1 0 BUILD_LIST 0 3 RETURN_VALUE